﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using TC.Vacation.Spider.Events;

namespace TC.Vacation.Spider.Core.Iocp
{
    public class TcpServer
    {
        private int t_numConnections;
        private int t_receiveBuffersize;
        private BufferManager bufferManager;
        private const int opsToPreAlloc = 2;
        private Socket listenSocket;
        private SocketAsyncEventArgsPool readWritePool;
        private SemaphoreSlim maxNumberAcceptedClients;
        private string t_serverAddress;
        int m_numConnectedSockets;
        /// <summary>
        /// 获取服务器地址(包括端口号)
        /// </summary>
        public string Address
        {
            get
            {
                return t_serverAddress;
            }
        }
        /// <summary>
        /// 服务器可接受的最大连接数
        /// </summary>
        public int MaxConnections
        {
            get
            {
                return t_numConnections;
            }
        }

        /// <summary>
        /// 缓冲区的容量
        /// </summary>
        public int TotalBytes
        {
            get
            {
                return bufferManager.TotayBytes;
            }
        }

        /// <summary>
        /// 当前活动客户端
        /// </summary>
        /// <value>
        /// The connections.
        /// </value>
        public int Connections
        {
            get
            {
                return m_numConnectedSockets;
            }
        }

        /// <summary>
        /// 构造函数初始化服务器
        /// </summary>
        /// <param name="numConnections">最大客户端数,默认单台服务器最大连接数为1000</param>
        /// <param name="receiveBufferSize">指定最小缓冲单位,该值过大或过小都会导致服务器性能的改变，如不知道如何配置，请使用默认值</param>
        public TcpServer(int numConnections = 1000, int receiveBufferSize = 64)
        {
            t_numConnections = numConnections;
            m_numConnectedSockets = 0;
            t_receiveBuffersize = receiveBufferSize;
            bufferManager = new BufferManager(receiveBufferSize * numConnections * opsToPreAlloc, receiveBufferSize);
            readWritePool = new SocketAsyncEventArgsPool(numConnections);
            maxNumberAcceptedClients = new SemaphoreSlim(numConnections, numConnections);
        }

        /// <summary>
        /// 初始化，开辟缓存空间和线程池
        /// </summary>
        internal void Init()
        {
            bufferManager.InitBuffer();
            SocketAsyncEventArgs readWriteEventArg;
            for (var i = 0; i < t_numConnections; i++)
            {
                readWriteEventArg = new SocketAsyncEventArgs();
                readWriteEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
                readWriteEventArg.UserToken = new AsyncUserToken();
                bufferManager.SetBuffer(readWriteEventArg);
                readWritePool.Push(readWriteEventArg);
            }
        }

        /// <summary>
        /// 启动服务器监听
        /// </summary>
        /// <param name="localip">本地监听IP地址</param>
        /// <param name="port">端口号,默认4444</param>
        public void Start(string localip, int port = 4444)
        {
            t_serverAddress = string.Format("{0}:{1}", localip, port);
            var localEndPoint = new IPEndPoint(IPAddress.Parse(localip), port);
            listenSocket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            listenSocket.Bind(localEndPoint);
            listenSocket.Listen(100);
            uint dummy = 0;

            byte[] inOptionValues = new byte[Marshal.SizeOf(dummy) * 3];
            BitConverter.GetBytes((uint)1).CopyTo(inOptionValues, 0);
            BitConverter.GetBytes((uint)5000).CopyTo(inOptionValues, Marshal.SizeOf(dummy));
            BitConverter.GetBytes((uint)5000).CopyTo(inOptionValues, Marshal.SizeOf(dummy) * 2);

            //在Windows中，第一次探测是在最后一次数据发送的两个小时，然后每隔1秒探测一次，一共探测5次
            //如果5次都没有收到回应的话，就会断开这个连接。此处改为5秒后开始探测，探测间隔10秒。
            //实现心跳包功能。
            listenSocket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);

            StartAccept(null);
        }

        /// <summary>
        /// 开始接收
        /// </summary>
        /// <param name="acceptEventArg"></param>
        internal void StartAccept(SocketAsyncEventArgs acceptEventArg)
        {
            if (acceptEventArg == null)
            {
                acceptEventArg = new SocketAsyncEventArgs();
                acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed);
            }
            else
            {
                acceptEventArg.AcceptSocket = null;
            }

            maxNumberAcceptedClients.Wait();
            bool willRaiseEvent = listenSocket.AcceptAsync(acceptEventArg);
            if (!willRaiseEvent)
            {
                ProcessAccept(acceptEventArg);
            }
        }

        /// <summary>
        /// 连接完成
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e)
        {
            ProcessAccept(e);
        }

        /// <summary>
        /// 接收客户端的连接
        /// </summary>
        /// <param name="e"></param>
        private void ProcessAccept(SocketAsyncEventArgs e)
        {
            Interlocked.Increment(ref m_numConnectedSockets);

            var readEventArgs = readWritePool.Pop();
            ((AsyncUserToken)readEventArgs.UserToken).Socket = e.AcceptSocket;
            bool willRaiseEvent = e.AcceptSocket.ReceiveAsync(readEventArgs);
            if (!willRaiseEvent)
            {
                ProcessReceive(readEventArgs);
            }
            StartAccept(e);
        }

        /// <summary>
        /// 执行完成后的回调
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void IO_Completed(object sender, SocketAsyncEventArgs e)
        {
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Receive:
                    ProcessReceive(e);
                    break;
                case SocketAsyncOperation.Send:
                    ProcessSend(e);
                    break;
            }
        }

        /// <summary>
        /// 接收客户端数据
        /// </summary>
        /// <param name="e"></param>
        private void ProcessReceive(SocketAsyncEventArgs e)
        {
            var token = (AsyncUserToken)e.UserToken;
            if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
            {
                e.SetBuffer(e.Offset, e.BytesTransferred);
                var message = Encoding.UTF8.GetString(e.Buffer, e.Offset, e.BytesTransferred);

                bool willRaiseEvent = token.Socket.SendAsync(e);
                if (!willRaiseEvent)
                {
                    ProcessSend(e);
                }
            }
            else
            {
                Close(e);
            }
        }

        /// <summary>
        /// 向客户端发送数据
        /// </summary>
        /// <param name="e"></param>
        private void ProcessSend(SocketAsyncEventArgs e)
        {
            if (e.SocketError == SocketError.Success)
            {
                var token = (AsyncUserToken)e.UserToken;
                bool willRaiseEvent = token.Socket.ReceiveAsync(e);
                if (!willRaiseEvent)
                {
                    ProcessReceive(e);
                }
            }
            else
            {
                Close(e);
            }
        }

        /// <summary>
        /// 关闭连接
        /// </summary>
        /// <param name="e"></param>
        public void Close(SocketAsyncEventArgs e)
        {
            var token = e.UserToken as AsyncUserToken;
            try
            {
                token.Socket.Shutdown(SocketShutdown.Send);
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                token.Socket.Close();
            }

            Interlocked.Decrement(ref m_numConnectedSockets);
            maxNumberAcceptedClients.Release();
            readWritePool.Push(e);
        }
    }
}
